﻿using System;
using System.Text;
using System.IO;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;

using alib.Debugging;
using alib.Enumerable;
using alib.String;
using alib.Collections.ReadOnly;
using alib.Reflection;
using alib.BitArray;

namespace agree
{
	public unsafe class MultiRootTfs : ArrayTfs, IReadOnlyList<TfsSlot>
	{
		public MultiRootTfs(IRtParent parent, TfsBuilder.Xdl xdlb)
			: base(parent, xdlb, xdlb.restrictor, Flags.MultiRoot)
		{
			var rgrme = xdlb.rgrme;

			this.c_roots = rgrme.Length;
			this.c_act = xdlb.EdgeCount + c_roots;
			this.entries = new arr_tfs_entry[c_act > 256 ? c_act : 256];

			fixed (ushort* phf = h.pfirst)
			fixed (arr_tfs_entry* _base = entries)
			{
				arr_tfs_entry* pate = _base;
				int i_ate = 0;
				for (; i_ate < c_roots; i_ate++)
				{
					pate->tgrm = rgrme[i_ate].rnd.tgrm;
					pate->e = rgrme[i_ate].rnd.e;
					pate++;
				}

				xdlb.CopyToArrayTfsEntries(pate);
				arr_tfs_entry* rels_base = pate;

				Debug.Assert(c_corefs == 0);
				int next_coref_mark = -1;
				for (; i_ate < c_act; i_ate++)
				{
					int m_old = pate->e_Mark;
					if (pate->e_FlagsId < 0 && m_old > 0)
					{
						short m_new = (short)next_coref_mark--;

						arr_tfs_entry* pate2 = rels_base;
						for (int j = c_roots; j < c_act; j++)
						{
							if (pate2->mark == m_old)
								pate2->mark = m_new;
							if (pate2->e_Mark == m_old)
								pate2->e_Mark = m_new;
							pate2++;
						}
					}
					pate++;
				}
				base.c_corefs = -next_coref_mark - 1;
				Debug.Assert(c_corefs >= 0);

				pate = rels_base;
				for (i_ate = c_roots; i_ate < c_act; )
				{
					i_ate++;
					int v = (byte)(pate->i_feat ^ pate->mark);
					pate->next = phf[v];
					phf[v] = (ushort)i_ate;
					pate++;
				}
			}
#if false
			var u = Unification.allocU();
			u._fixup_multi_root_corefs(xdlb, xdlb, this);
#endif
		}

		public int c_roots;
		public int Count { get { return c_roots; } }

		protected IReadOnlyList<TfsSlot> Roots { get { return (IReadOnlyList<TfsSlot>)this; } }

		public override void GetIndexData(int ix, arr_tfs_entry* pate)
		{
			*pate = entries[ix];
		}

		TfsSlot IReadOnlyList<TfsSlot>.this[int ix]
		{
			get
			{
				if (ix >= c_roots)
					throw new IndexOutOfRangeException();
				return new TfsSlot(this, ix);
			}
		}

		IEnumerator<TfsSlot> IEnumerable<TfsSlot>.GetEnumerator()
		{
			for (int i = 0; i < c_roots; i++)
				yield return new TfsSlot(this, i);
		}

		IEnumerator IEnumerable.GetEnumerator()
		{
			return ((IEnumerable<TfsSlot>)this).GetEnumerator();
		}



#if REVERSE_INDEX
		protected override ReadOnlyDictionary<int, int[]> _rebuild_reverse_index()
		{
			int[] non_coref = new int[entries.Length];	/// fix fix
			int ncm_max = int.MinValue;
			List<int>[] L_coref = new List<int>[c_corefs];

			for (int ix = 0; ix < c_act; )
			{
				int om = entries[ix].e_Mark;
				ix++;
				if (om > 0)
				{
					non_coref[om] = ix;
					if (om > ncm_max)
						ncm_max = om;
				}
				else if (om < 0)
					(L_coref[~om] ?? (L_coref[~om] = new List<int>())).Add(ix);
			}
			return non_coref
					.Take(ncm_max + 1)
					.TagIndex()
					.Where(a => a.Item != 0)
					.Select(a => new KeyValuePair<int, int[]>(a.Index, new int[] { a.Item }))
					.Concat(L_coref.Select((L, notm) => new KeyValuePair<int, int[]>(~notm, alib.Enumerable._enumerable_ext._ToArray(L))))
					.ToReadOnlyDictionary();
		}

		public override Edge GetCorefEdge(int out_mark)
		{
			int[] ret;
			if (!_reverse_index.TryGetValue(out_mark, out ret))
				return default(Edge);

			return entries[ret[0] - 1].e;
		}
#endif
	};
}